package com.nirvana.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.nirvana.admin.entity.SysMenu;
import com.nirvana.admin.entity.SysRole;
import com.nirvana.admin.entity.SysRoleMenu;
import com.nirvana.admin.entity.SysUserRole;
import com.nirvana.admin.entity.vo.AuthMenuResponseVO;
import com.nirvana.admin.entity.vo.LoginUser;
import com.nirvana.admin.mapper.SysMenuMapper;
import com.nirvana.admin.mapper.SysRoleMenuMapper;
import com.nirvana.admin.mapper.SysUserRoleMapper;
import com.nirvana.admin.service.SysMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nirvana.admin.service.SysRoleService;
import com.nirvana.admin.util.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单权限表 服务实现类
 * </p>
 *
 * @author zhangshuaiyin
 * @since 2021-09-29
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Resource
    private SysUserRoleMapper userRoleMapper;
    @Resource
    private SysRoleMenuMapper roleMenuMapper;
    @Resource
    private SysRoleService roleService;

    private final static Integer MENU_TYPE = 1;
    private final static Integer CATE_TYPE = 2;
    private final static Integer BUTTON_TYPE = 3;

    private final static String ACTIVE_STATUS = "0";

    @Override
    public List<SysMenu> getList(List<SysRole> roleList) {
        // 管理员拥有所有菜单权限
        if (roleService.hasAnyAdmin(roleList)) {
            return baseMapper.selectList(null);
        }
        // 查询角色对应目录、菜单、按钮&状态可用
        Set<Long> menuIds = getMenuIds(roleList);
        return baseMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                .in(SysMenu::getId, menuIds)
                .in(SysMenu::getMenuType, Arrays.asList(MENU_TYPE, CATE_TYPE, BUTTON_TYPE))
                .eq(SysMenu::getStatus, ACTIVE_STATUS));
    }

    @Override
    public List<SysMenu> getMenus(List<SysRole> roleList) {
        // 管理员拥有所有菜单权限
        if (roleService.hasAnyAdmin(roleList)) {
            return baseMapper.selectList(Wrappers.<SysMenu>lambdaQuery()
                    .in(SysMenu::getMenuType, Arrays.asList(MENU_TYPE, CATE_TYPE))
                    .eq(SysMenu::getStatus, ACTIVE_STATUS));
        }
        // 查询角色对应目录、菜单 &状态可用
        Set<Long> menuIds = getMenuIds(roleList);
        return baseMapper.selectList(new LambdaQueryWrapper<SysMenu>()
                .in(SysMenu::getId, menuIds)
                .in(SysMenu::getMenuType, Arrays.asList(MENU_TYPE, CATE_TYPE))
                .eq(SysMenu::getStatus, ACTIVE_STATUS));
    }

    @Override
    public List<AuthMenuResponseVO> getRouters() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        // 获取菜单信息 目录、菜单
        assert loginUser != null;
        List<SysMenu> menuList = this.getMenus(roleService.listByIds(getUserRoleIds(loginUser)));

        // 树化返回
        return treeify(menuList);
    }

    @Override
    public List<AuthMenuResponseVO> findList() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        // 获取菜单信息 目录、菜单、按钮
        assert loginUser != null;
        return treeify(this.getList(roleService.listByIds(getUserRoleIds(loginUser))));
    }

    private List<AuthMenuResponseVO> treeify(List<SysMenu> menuList) {
        List<AuthMenuResponseVO> treeNodeList = menuList.stream()
                .sorted(Comparator.comparing(SysMenu::getSort))
                .map(menu -> {
                    AuthMenuResponseVO menuVO = new AuthMenuResponseVO();
                    BeanUtils.copyProperties(menu, menuVO);
                    return menuVO;
                }).collect(Collectors.toList());

        return treeNodeList.stream()
                .filter(node -> node.getParentId() == 0L)
                .peek(node -> node.setChildren(getChildren(node, treeNodeList)))
                .collect(Collectors.toList());
    }

    private List<AuthMenuResponseVO> getChildren(AuthMenuResponseVO root, List<AuthMenuResponseVO> treeNodeList) {
        return treeNodeList.stream()
                .filter(node -> node.getParentId().equals(root.getId()))
                .peek(node -> node.setChildren(getChildren(node, treeNodeList)))
                .collect(Collectors.toList());
    }

    private Set<Long> getUserRoleIds(LoginUser loginUser) {
        List<SysUserRole> userRoleList = userRoleMapper.selectList(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, loginUser.getId()));
        return userRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
    }

    private Set<Long> getMenuIds(List<SysRole> roleList) {
        Set<Long> roleIdList = roleList.stream().map((SysRole::getId)).collect(Collectors.toSet());
        List<SysRoleMenu> roleMenuList = roleMenuMapper.selectList(new LambdaQueryWrapper<SysRoleMenu>().in(SysRoleMenu::getRoleId, roleIdList));
        return roleMenuList.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toSet());
    }
}
